﻿using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Windows.Input;
using Amazon.SimpleDB;
using Amazon.SimpleDB.Model;

namespace SimpleDbBrowser.ViewModel
{
    /// <summary>
    /// The view model responsible for exposing the PutAttribute command.
    /// </summary>
    public sealed class PutAttributeViewModel : ViewModelBase
    {
        private readonly AmazonSimpleDB db;

        private string domain;
        private Item item;
        private string itemName;
        private string name;
        private string value;

        /// <summary>
        /// Initializes a new instance of the PutAttributeViewModel class.
        /// </summary>
        /// <param name="database"></param>
        public PutAttributeViewModel(AmazonSimpleDB database)
        {
            this.DisplayName = "Add an attribute to an item";
            if (database == null) throw new ArgumentNullException("database");

            this.db = database;

            this.PutAttribute = new RelayCommand(this.OnPutAttribute, this.CanPutAttribute);
        }

        /// <summary>
        /// Gets or sets the name of the attribute being added.
        /// </summary>
        public string AttributeName
        {
            get { return this.name; }

            set
            {
                if (this.name == value) return;

                this.name = value;
                this.OnPropertyChanged("AttributeName");
                Debug.WriteLine(string.Format("PutAttributeViewModel.AttributeName set to {0}", this.name));
            }
        }

        /// <summary>
        /// Gets or sets the value of the attribute being added.
        /// </summary>
        public string AttributeValue
        {
            get { return this.value; }

            set
            {
                if (this.value == value) return;

                this.value = value;
                this.OnPropertyChanged("AttributeValue");
                Debug.WriteLine(string.Format("PutAttributeViewModel.AttributeValue set to {0}", this.value));
            }
        }

        /// <summary>
        /// Gets or sets the name of the item that the attribute is being 
        /// added to.
        /// </summary>
        public string ItemName
        {
            get { return this.itemName; }

            set
            {
                if (this.itemName == value) return;

                this.itemName = value;
                this.OnPropertyChanged("ItemName");
                Debug.WriteLine(string.Format("PutattributeViewModel.ItemName set to {0}", this.itemName));
            }
        }

        /// <summary>
        /// Gets or sets the name of the domain that the attribute is being 
        /// added to.
        /// </summary>
        public string SelectedDomain
        {
            get { return this.domain; }

            set
            {
                if (this.domain == value) return;

                this.domain = value;
                this.OnPropertyChanged("SelectedDomain");
                Debug.WriteLine(string.Format("PutAttributeViewModel.SelectedDomain set to {0}", this.domain));
            }
        }

        /// <summary>
        /// Gets or sets the currently selected item.
        /// </summary>
        /// <remarks>
        /// If this is changed, it may change the ItemName.
        /// </remarks>
        public Item SelectedItem
        {
            get { return this.item; }

            set
            {
                if (this.item == value) return;

                this.item = value;
                this.OnSelectedItemChanged();
                Debug.WriteLine(string.Format("PutAttributeViewModel.SelectedItem set to {0}", this.item));
            }
        }

        /// <summary>
        /// Gets the PutAttribute command.
        /// </summary>
        public ICommand PutAttribute { get; private set; }
        
        /// <summary>
        /// Fires once the attributes have been added.
        /// </summary>
        public event EventHandler AttributePut;

        /// <summary>
        /// Executes the PutAttribute command.
        /// </summary>
        /// <param name="param">
        /// This is not used.
        /// </param>
        private void OnPutAttribute(object param)
        {
            var n = this.name;
            var v = this.value;
            var d = this.domain;

            Debug.Assert(!(string.IsNullOrWhiteSpace(n) || string.IsNullOrWhiteSpace(v) || string.IsNullOrWhiteSpace(d)));

            var worker = new BackgroundWorker();

            worker.DoWork += (o, a) =>
                             {
                                 var req = new PutAttributesRequest().WithDomainName(d).WithItemName(this.itemName).
                                         WithAttribute(new ReplaceableAttribute().WithName(n).WithValue(v));
                                 var resp = this.db.PutAttributes(req);
                             };

            worker.RunWorkerCompleted += (o, a) => this.OnAttributePut(EventArgs.Empty);

            worker.RunWorkerAsync();
        }

        /// <summary>
        /// Determines whether the PutAttribute command can be executed.
        /// </summary>
        /// <param name="o">
        /// This is not used.
        /// </param>
        /// <returns>
        /// True if the command can be executed, otherwise false.
        /// </returns>
        private bool CanPutAttribute(object o)
        {
            return !string.IsNullOrWhiteSpace(this.name) && !string.IsNullOrWhiteSpace(this.value) &&
                   !string.IsNullOrWhiteSpace(this.value);
        }

        /// <summary>
        /// Executes actions that must occur when the selected item is changed.
        /// </summary>
        private void OnSelectedItemChanged()
        {
            this.OnPropertyChanged("SelectedItem");
            this.ItemName = this.item != null ? this.item.Name : string.Empty;
        }

        /// <summary>
        /// Fires the AttributePut event.
        /// </summary>
        /// <param name="args">
        /// AttributePut event args.
        /// </param>
        private void OnAttributePut(EventArgs args)
        {
            var tmp = this.AttributePut;
            if (tmp != null)
            {
                tmp(this, args ?? EventArgs.Empty);
            }

            this.AttributeName = string.Empty;
            this.AttributeValue = string.Empty;

            return;
        }
    }
}